#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <assert.h>
#include <string.h>

#define MAX_LEN 200;
// this should be enough
static char buf[65536];
static int isMax;
uint64_t ind = 0;
static int divf = 0;
// uint64_t counter;
// char opsc[]= {'+','*','-','/'};
static const char* ops[] = {"+","*","==","!=","&&","-","/"};
static int expr_cnt =0;

static uint32_t choose(uint32_t n) {
  return rand() % n;
}

static void gen_num() {
  // buf[ind++] = choose(10) + '0'; //divf ? choose(9) + '1': choose(10) + '0';
  uint16_t len;
  len = choose(2)+1;

  buf[ind++] = len > 1 || divf ? choose(9) + '1': choose(10) + '0';
  for (int i = 1; i < len; i++) {
    buf[ind++] = divf ? choose(9) + '1': choose(10) + '0';
  }
}

static void gen_rand_op() {
  // buf[ind++] = ops[choose(4)]; //choose(4)
  // buf[ind++] = divf ? ops[choose(2)]: ops[choose(4)]; //choose(4)
  uint8_t indc = divf ? choose(5) : choose(7);
  strncpy(buf+ind,ops[indc],strlen(ops[indc]));
  ind += strlen(ops[indc]);
}

static void gen_rand_expr() {
  uint32_t c = choose(3);
  isMax = (expr_cnt >=30 || ind >= 200);
  if (isMax) {
    return;
  }
  switch (c) {
    case 0: gen_num(); expr_cnt++; break;
    case 1: buf[ind++] = '('; expr_cnt++; gen_rand_expr(); buf[ind++] = ')'; expr_cnt++;break;
    // default: gen_rand_expr(); gen_rand_op();gen_rand_expr(); break;
    default:  
      gen_rand_expr(); 
      gen_rand_op();
      expr_cnt++; 
      if (buf[ind - 1] == '/') 
        divf = 1;
      gen_rand_expr(); 
      divf = 0;
      break;
  }
}

static char code_buf[65536];
static char *code_format =
"#include <stdio.h>\n"
"int main() { "
"  unsigned result = %s; "
"  printf(\"%%u\", result); "
"  return 0; "
"}";

int main(int argc, char *argv[]) {
  int seed = time(0);
  srand(seed);
  int loop = 1;
  if (argc > 1) {
    sscanf(argv[1], "%d", &loop);
  }
  int i;

  for (i = 0; i < loop; i ++) {
    ind = 0;
    isMax = 0;
    expr_cnt = 0;
    memset(buf,0,sizeof(buf));
    gen_rand_expr();
    if (isMax) {
      continue;
    }
    
    sprintf(code_buf, code_format, buf);
    FILE *fp = fopen("/tmp/.code.c", "w");
    assert(fp != NULL);
    fputs(code_buf, fp);
    fclose(fp);

    int ret = system("gcc /tmp/.code.c -o /tmp/.expr");
    if (ret != 0) continue;

    fp = popen("/tmp/.expr", "r");
    assert(fp != NULL);

    int result;

    fscanf(fp, "%d", &result);
    pclose(fp);

    printf("%u %s\n", result, buf);
  }
  return 0;
}
